<?php

namespace app\bll;

use BlueM\Tree;
use core\lib\schema\Schema;
use core\lib\schema\Table;
use core\lib\upload\UploadFile as LibUploadFile;
use Exception;
use hulang\tool\FileHelper;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Melihovv\Base64ImageDecoder\Base64ImageEncoder;
use PhpZip\ZipFile;
use support\Db;
use Throwable;
use app\lib\installer\General as LibInstallerGeneral;
use core\lib\tool\File;

/**
 * 应用插件
 */
class Plugin
{

    /**
     * 查询一个插件信息
     *
     * @param integer $id
     * @return array
     */
    public function find(int $id): array
    {
        $data =  Db::table('frame_plugin')
            ->where('id', '=', $id)
            ->first();
        return $data === null ? [] : get_object_vars($data);
    }

    /**
     * 查询应用插件
     *
     * @return array
     */
    public function select(): array
    {
        return Db::table('frame_plugin')
            ->get()
            ->map(function ($value) {
                return (array)$value;
            })
            ->toArray();
    }



    /**
     * 安装和升级的前置操作
     *
     * @return array
     */
    private function beforeInstallUpgrade(string $type = 'install'): array
    {
        $extractpath = '';
        try {
            //压缩包上传
            $libUploadFile = new LibUploadFile(
                ispublic: false,
                path: 'plugin'
            );
            $fileinfo = $libUploadFile->upload();

            //获取安装或更新的插件名称和版本
            $libInstallerGeneral = new LibInstallerGeneral();
            $plugininfo          = $libInstallerGeneral->getNameAndVersion($fileinfo['orgname']);
            $plugininfo['ident'] = $plugininfo['name'];

            //释放压缩包
            $extractpath = $this->extractZip($fileinfo, $plugininfo['ident'], $type);

            //检查安装菜单
            $install_database_menus =  $this->checkInstallMenu($extractpath . '/menu.php', $plugininfo['ident']);
            $install_database_ucmenus =  $this->checkInstallUcMenu($extractpath . '/ucmenu.php', $plugininfo['ident']); //用户中心菜单


            //检查安装事件动作
            $intsall_hooks = $this->checkHook($extractpath . '/hook.php', $plugininfo['ident']);


            //安装数据库 安装数据库不支持数据回滚 安全起见 出错后手动删除
            $this->installDataBase($extractpath . '/install.sql');
        } catch (Exception $ex) {
            //删除压缩包和文件
            if (isset($fileinfo['path'])) {
                $this->cleanInstallUpgradeFiles(
                    $extractpath,
                    base_path($fileinfo['path'])
                );
            }
            throw $ex;
        }

        return [
            'fileinfo'                 => $fileinfo,
            'extractpath'              => $extractpath,
            'install_database_menus'   => $install_database_menus,
            'install_database_ucmenus' => $install_database_ucmenus,
            'intsall_hooks'            => $intsall_hooks,
            'plugininfo'               => $plugininfo
        ];
    }
    /**
     * 安装应用插件
     * 
     * 
     * @return void
     */
    public function install()
    {


        $extractpath            = '';  //解压目录
        $fileinfo               = [];  //上传的文件信息
        $install_database_menus = [];  //菜单安装信息
        $intsall_hooks          = [];  //事件动作
        $plugininfo             = [];  //插件信息 包含名称和版本

        try {
            //检查上传文件
            $this->checkInstallUpload();

            $basicdata                = $this->beforeInstallUpgrade();
            $extractpath              = $basicdata['extractpath'];
            $fileinfo                 = $basicdata['fileinfo'];
            $install_database_menus   = $basicdata['install_database_menus'];
            $install_database_ucmenus = $basicdata['install_database_ucmenus'];
            $intsall_hooks            = $basicdata['intsall_hooks'];
            $plugininfo               = $basicdata['plugininfo'];
        } catch (Exception $ex) {
            //删除压缩包和文件
            if (isset($fileinfo['path'])) {
                $this->cleanInstallUpgradeFiles(
                    $extractpath,
                    base_path($fileinfo['path'])
                );
            }
            throw $ex;
        }

        Db::beginTransaction();
        try {


            //拷贝文件
            $pluginpath = base_path('/plugin/' . $plugininfo['ident']); //插件目录
            mkdir($pluginpath, 0777, true);
            FileHelper::copyDir($extractpath . '/source', base_path());


            //插入到插件
            $describe = $this->getDescribe($plugininfo['ident']);
            Db::table('frame_plugin')
                ->insertGetId($describe);

            // 安装菜单
            $this->installMenu($install_database_menus);
            // 安装用户中心菜单
            $this->installUcMenu($install_database_ucmenus);
            //安装 Hook
            $this->installHook($intsall_hooks);

            Db::commit();
        } catch (Throwable $ex) {

            Db::rollBack();
            throw $ex;
        } finally {

            //删除压缩包和文件
            $this->cleanInstallUpgradeFiles(
                $extractpath,
                base_path($fileinfo['path'])
            );
        }
    }


    /**
     * 升级
     * @return void
     */
    public function handleUpgrade(int $id)
    {
        $extractpath            = '';  //解压目录
        $fileinfo               = [];  //上传的文件信息
        $install_database_menus = [];  //菜单安装信息
        $intsall_hooks          = [];  //事件动作
        $plugininfo             = [];  //插件信息 包含名称和版本
        try {
            $basicdata                = $this->beforeInstallUpgrade('upgrade');
            $extractpath              = $basicdata['extractpath'];
            $fileinfo                 = $basicdata['fileinfo'];
            $install_database_menus   = $basicdata['install_database_menus'];
            $install_database_ucmenus = $basicdata['install_database_ucmenus'];
            $intsall_hooks            = $basicdata['intsall_hooks'];
            $plugininfo               = $basicdata['plugininfo'];
        } catch (Exception $ex) {
            //删除压缩包和文件
            if (isset($fileinfo['path'])) {
                $this->cleanInstallUpgradeFiles(
                    $extractpath,
                    base_path($fileinfo['path'])
                );
            }
            throw $ex;
        }

        Db::beginTransaction();
        try {
            $thisplugininfo = $this->find($id);

            if (version_compare($plugininfo['toversion'], $thisplugininfo['version'], '<=')) {
                throw new Exception('升级版本不能小于当前版本');
            }

            if (!version_compare($plugininfo['fromversion'], $thisplugininfo['version'], '=')) {
                throw new Exception('升级开始版本和当前版本不一致');
            }


            //拷贝文件
            FileHelper::copyDir($extractpath . '/source', base_path());
            $describe = $this->getDescribe($thisplugininfo['ident']);
            //修改插件信息
            $describe['nextversion'] = '';
            Db::table('frame_plugin')
                ->where('id', '=', $id)
                ->where('ident', '=', $thisplugininfo['ident'])
                ->update($describe);

            // 安装菜单
            $this->installMenu($install_database_menus);
            $this->installUcMenu($install_database_ucmenus);
            //安装 Hook
            $this->installHook($intsall_hooks);

            Db::commit();
        } catch (Throwable $ex) {
            Db::rollBack();
            throw $ex;
        } finally {

            //删除压缩包和文件
            $this->cleanInstallUpgradeFiles(
                $extractpath,
                base_path($fileinfo['path'])
            );
        }
    }



    public function onlineUpgrade(int $id)
    {
        $thisplugininfo = $this->find($id);
        if (! $thisplugininfo) {
            throw new Exception('应用不存在');
        }

        if (! $thisplugininfo['upgrade']) {
            throw new Exception('应用没有升级地址');
        }



        $upgrade = $thisplugininfo['upgrade'];
        $libInstallerGeneral = new LibInstallerGeneral();

        $nextVersion = $libInstallerGeneral->getNextUpgradeVersion($upgrade, $thisplugininfo['version']);
        if (!$nextVersion) {
            throw new Exception('没有找到升级包');
        }

        //下载升级包
        $downpath = File::downloadRemoteFile($nextVersion['upgradeUrl'], 'upgrade');

        $zipFile = new ZipFile();
        $zipFile->openFile($downpath);

        //释放升级包
        $extractpath = runtime_path() . '/storage/upgrade/plugin/' . $thisplugininfo['ident'] . '/' . time();
        if (!file_exists($extractpath)) {
            mkdir($extractpath, 0777, true);
        }

        $zipFile->extractTo($extractpath);


        //执行SQL
        $this->executeSqlFile($extractpath . '/install.sql');


        Db::beginTransaction();
        try {


            //检查安装菜单
            $install_database_menus =  $this->checkInstallMenu($extractpath . '/menu.php', $thisplugininfo['ident']);
            $install_database_ucmenus =  $this->checkInstallMenu($extractpath . '/ucmenu.php', $thisplugininfo['ident']);


            //检查安装事件动作
            $intsall_hooks = $this->checkHook($extractpath . '/hook.php', $thisplugininfo['ident']);

            //拷贝文件
            FileHelper::copyDir($extractpath . '/source', base_path());
            $describe = $this->getDescribe($thisplugininfo['ident']);
            //修改插件信息
            $describe['nextversion'] = '';
            Db::table('frame_plugin')
                ->where('id', '=', $id)
                ->where('ident', '=', $thisplugininfo['ident'])
                ->update($describe);

            // 安装菜单
            $this->installMenu($install_database_menus);
            // 安装用户中心菜单
            $this->installUcMenu($install_database_ucmenus);

            //安装 Hook
            $this->installHook($intsall_hooks);


            Db::commit();
        } catch (Exception $ex) {
            Db::rollBack();
            throw $ex;
        }
    }


    /**
     * 卸载
     *
     * @param integer $id
     * @return void
     */
    public function unInstall(int $id)
    {

        $plugininfo = $this->find($id);

        Db::beginTransaction();
        try {
            $bllAdminMenu = new AdminMenu();
            $adminmenus = $bllAdminMenu->select(
                ident: [$plugininfo['ident']]
            );

            $adminmenusidents = array_column($adminmenus, 'ident');


            //删除菜单后台
            Db::table('frame_admin_menu')
                ->where('plugin', '=', $plugininfo['ident'])
                ->delete();


            //删除菜单用户中心
            Db::table('frame_ucenter_menu')
                ->where('plugin', '=', $plugininfo['ident'])
                ->delete();


            //删除事件动作
            Db::table('frame_hook')
                ->where('plugin', '=', $plugininfo['ident'])
                ->delete();

            //删除用户角色里的菜单
            if ($adminmenusidents) {
                Db::table('frame_site_role_menu')
                    ->whereIn('ident', $adminmenusidents)
                    ->delete();
            }

            Db::commit();
        } catch (Throwable $ex) {
            Db::rollBack();
            throw $ex;
        }

        //删除表
        $tables = Schema::tables();
        foreach ($tables as $table) {
            if (str_starts_with($table['name'], getDatabasePrefix() . $plugininfo['ident'] . '_')) {
                $droptablename = substr($table['name'], strlen(getDatabasePrefix()));
                Schema::dropTable($droptablename);
            }
        }

        //移除文件夹
        $deldirs = [
            '/template/admin/' . $plugininfo['ident'],
            '/template/uc/' . $plugininfo['ident'],
            '/public/static/admin/' . $plugininfo['ident'],
            '/public/static/uc/' . $plugininfo['ident'],
            '/plugin/' . $plugininfo['ident'],
        ];

        foreach ($deldirs as $dir) {
            if (is_dir(base_path() . $dir) && file_exists(base_path() . $dir)) {
                FileHelper::delDir(base_path() . $dir);
            }
        }

        //删除插件
        Db::table('frame_plugin')
            ->where('id', '=', $id)
            ->delete();
    }


    /**
     * 检查安装上传的压缩包
     *
     * @return void
     */
    private function checkInstallUpload()
    {
        //检查
        $spl_file = array_values(request()->file());
        $ext = '.' . $spl_file[0]->getUploadExtension();
        $pluginname = substr($spl_file[0]->getUploadName(), 0, -strlen($ext));

        if (file_exists(base_path() . '/plugin/' . $pluginname)) {

            throw new Exception('插件已存在');
        }
    }



    /**
     * 检查安装菜单
     *
     * @param string $menufile
     * @return array
     */
    private function checkInstallMenu(string $menufile, string $pluginname): array
    {
        if (file_exists($menufile)) {
            $installmenus = include $menufile;

            //获取数据库所有的菜单 ident做主键
            $bllAdminMenu  = new AdminMenu();
            $databasemenus = $bllAdminMenu->select();
            $databasemenus = array_column($databasemenus, null, 'ident');

            /**判断删除的菜单 */
            $delmenus = [];
            if (isset($installmenus['delete']) && $installmenus['delete']) {

                $delmenus = $installmenus['delete'];
            }


            /**判断添加的菜单 */
            $addmenus = [];
            if (isset($installmenus['add']) && $installmenus['add']) {
                foreach ($installmenus['add'] as $key => $imenu) {

                    if (trim($imenu['title']) == '') {
                        throw new Exception('菜单名不能为空');
                    }
                    if (trim($imenu['ident']) == '') {
                        throw new Exception('菜单' . $imenu['title'] . '标识不能为空');
                    }
                    $addmenus[] = [
                        'ident'    => $imenu['ident'],
                        'title'    => $imenu['title'],
                        'pident'   => isset($imenu['pident']) ? $imenu['pident'] : '',
                        'url'      => isset($imenu['url']) ? $imenu['url'] : '',
                        'icon'     => isset($imenu['icon']) ? $imenu['icon'] : '',
                        'type'     => isset($imenu['type']) ? $imenu['type'] : 0,
                        'sort'     => isset($imenu['sort']) ? $imenu['sort'] : 0,
                        'belong'   => isset($imenu['belong']) ? $imenu['belong'] : 1,
                        'plugin'   => $pluginname
                    ];
                }

                $addmenuidents = array_column($addmenus, 'ident');

                foreach ($installmenus['add'] as $key => $imenu) {
                    if ($imenu['pident'] !== '' && in_array($imenu['pident'], $delmenus)) {
                        throw new Exception('菜单 "' . $imenu['title'] . '"父标识错误(找不到)');
                    }

                    if (
                        $imenu['pident'] !== '' &&
                        !in_array($imenu['pident'], $addmenuidents) &&
                        !key_exists($imenu['pident'], $databasemenus)
                    ) {
                        throw new Exception('菜单"' . $imenu['title'] . '"父标识错误(找不到)');
                    }
                }
            }


            /**判断编辑的菜单 */
            $editmenus = [];
            if (isset($installmenus['edit']) && $installmenus['edit']) {
                foreach ($installmenus['edit'] as $key => $imenu) {

                    if (trim($imenu['title']) == '') {
                        throw new Exception('菜单名不能为空');
                    }
                    if (trim($imenu['ident']) == '') {
                        throw new Exception('菜单' . $imenu['title'] . '标识不能为空');
                    }
                    $editmenus[] = [
                        'ident'    => $imenu['ident'],
                        'title'    => $imenu['title'],
                        'pident'   => isset($imenu['pident']) ? $imenu['pident'] : '',
                        'url'      => isset($imenu['url']) ? $imenu['url'] : '',
                        'icon'     => isset($imenu['icon']) ? $imenu['icon'] : '',
                        'type'     => isset($imenu['type']) ? $imenu['type'] : 0,
                        'sort'     => isset($imenu['sort']) ? $imenu['sort'] : 0,
                        'belong'   => isset($imenu['belong']) ? $imenu['belong'] : 1,
                        'plugin'   => $pluginname
                    ];
                }

                $editmenuidents = array_column($editmenus, 'ident');

                foreach ($installmenus['edit'] as $key => $imenu) {
                    if (
                        $imenu['pident'] !== '' &&
                        in_array($imenu['pident'], $delmenus)
                    ) {
                        throw new Exception('菜单' . $imenu['title'] . '父标识错误(找不到)');
                    }

                    if (
                        $imenu['pident'] !== '' &&
                        !in_array($imenu['pident'], $addmenuidents)
                        && !key_exists($imenu['pident'], $editmenuidents)
                    ) {
                        throw new Exception('菜单' . $imenu['title'] . '父标识错误(找不到)');
                    }
                }
            }

            return [
                'delmenus'  => $delmenus,
                'addmenus'  => $addmenus,
                'editmenus' => $editmenus,
            ];
        }

        return [];
    }


    /**
     * 检查安装用户中心菜单
     *
     * @param string $menufile
     * @return array
     */
    private function checkInstallUcMenu(string $menufile, string $pluginname): array
    {
        if (file_exists($menufile)) {
            $installmenus = include $menufile;

            //获取数据库所有的菜单 ident做主键
            $bllUcenterMenu  = new UcenterMenu();
            $databasemenus = $bllUcenterMenu->select();
            $databasemenus = array_column($databasemenus, null, 'ident');

            /**判断删除的菜单 */
            $delmenus = [];
            if (isset($installmenus['delete']) && $installmenus['delete']) {

                $delmenus = $installmenus['delete'];
            }


            /**判断添加的菜单 */
            $addmenus = [];
            if (isset($installmenus['add']) && $installmenus['add']) {
                foreach ($installmenus['add'] as $key => $imenu) {

                    if (trim($imenu['title']) == '') {
                        throw new Exception('菜单名不能为空');
                    }
                    if (trim($imenu['ident']) == '') {
                        throw new Exception('菜单' . $imenu['title'] . '标识不能为空');
                    }
                    $addmenus[] = [
                        'ident'    => $imenu['ident'],
                        'title'    => $imenu['title'],
                        'pident'   => isset($imenu['pident']) ? $imenu['pident'] : '',
                        'url'      => isset($imenu['url']) ? $imenu['url'] : '',
                        'icon'     => isset($imenu['icon']) ? $imenu['icon'] : '',
                        'type'     => isset($imenu['type']) ? $imenu['type'] : 0,
                        'sort'     => isset($imenu['sort']) ? $imenu['sort'] : 0,
                        'plugin'   => $pluginname
                    ];
                }

                $addmenuidents = array_column($addmenus, 'ident');

                foreach ($installmenus['add'] as $key => $imenu) {
                    if ($imenu['pident'] !== '' && in_array($imenu['pident'], $delmenus)) {
                        throw new Exception('菜单 "' . $imenu['title'] . '"父标识错误(找不到)');
                    }

                    if (
                        $imenu['pident'] !== '' &&
                        !in_array($imenu['pident'], $addmenuidents) &&
                        !key_exists($imenu['pident'], $databasemenus)
                    ) {
                        throw new Exception('菜单"' . $imenu['title'] . '"父标识错误(找不到)');
                    }
                }
            }


            /**判断编辑的菜单 */
            $editmenus = [];
            if (isset($installmenus['edit']) && $installmenus['edit']) {
                foreach ($installmenus['edit'] as $key => $imenu) {

                    if (trim($imenu['title']) == '') {
                        throw new Exception('菜单名不能为空');
                    }
                    if (trim($imenu['ident']) == '') {
                        throw new Exception('菜单' . $imenu['title'] . '标识不能为空');
                    }
                    $editmenus[] = [
                        'ident'    => $imenu['ident'],
                        'title'    => $imenu['title'],
                        'pident'   => isset($imenu['pident']) ? $imenu['pident'] : '',
                        'url'      => isset($imenu['url']) ? $imenu['url'] : '',
                        'icon'     => isset($imenu['icon']) ? $imenu['icon'] : '',
                        'type'     => isset($imenu['type']) ? $imenu['type'] : 0,
                        'sort'     => isset($imenu['sort']) ? $imenu['sort'] : 0,
                        'plugin'   => $pluginname
                    ];
                }

                $editmenuidents = array_column($editmenus, 'ident');

                foreach ($installmenus['edit'] as $key => $imenu) {
                    if (
                        $imenu['pident'] !== '' &&
                        in_array($imenu['pident'], $delmenus)
                    ) {
                        throw new Exception('菜单' . $imenu['title'] . '父标识错误(找不到)');
                    }

                    if (
                        $imenu['pident'] !== '' &&
                        !in_array($imenu['pident'], $addmenuidents)
                        && !key_exists($imenu['pident'], $editmenuidents)
                    ) {
                        throw new Exception('菜单' . $imenu['title'] . '父标识错误(找不到)');
                    }
                }
            }

            return [
                'delmenus'  => $delmenus,
                'addmenus'  => $addmenus,
                'editmenus' => $editmenus,
            ];
        }

        return [];
    }


    /**
     * 检查安装的动作事件
     *
     * @param string $eventfile
     * @return array
     */
    private function  checkHook(string $hookfile, string $pluginname): array
    {

        if (file_exists($hookfile)) {
            $installhooks = include $hookfile;

            $bllHook       = new Hook();
            $databaseHooks = $bllHook->select();
            $databaseHooks = array_column($databaseHooks, null, 'ident');


            if (isset($installhooks['add']) && $installhooks['add']) {
                foreach ($installhooks['add'] as $ihook) {
                    if (
                        key_exists($ihook['ident'], $databaseHooks)
                    ) {
                        throw new Exception('添加的事件动作HOOK' . $ihook['title'] . '已经存在');
                    }
                }
            }



            return $installhooks;
        }
        return [];
    }


    /**
     * 安装SQL
     *
     * @return void
     */
    private function  installDataBase($sqlfile)
    {
        if (file_exists($sqlfile)) {
            try {
                $this->executeSqlFile($sqlfile);
            } catch (Exception $ex) {
                throw $ex;
            }
        }
    }

    /**
     * 执行SQL 文件
     *
     * @param string $sqlfielpath
     * @return void
     */
    private function executeSqlFile(string $sqlfielpath)
    {

        try {
            if (file_exists($sqlfielpath)) {
                $sql = file_get_contents($sqlfielpath);
                $sql = str_replace('{{prefix}}', getDatabasePrefix(), $sql);
                if (trim($sql) != '') {
                    Db::unprepared($sql);
                }
            }
        } catch (Exception $ex) {
            throw $ex;
        }
    }




    /**
     * 安装菜单
     *
     * @param array $pluginname
     * @return void
     */
    private function installMenu(array $menus)
    {
        Db::beginTransaction();
        try {

            //删除 
            if (isset($menus['delmenus']) && $menus['delmenus']) {
                foreach ($menus['delete'] as $key => $imenu) {
                    Db::table('frame_admin_menu')
                        ->where('ident', '=', $imenu['ident'])
                        ->delete();
                }
            }


            //新增
            if (isset($menus['addmenus']) && $menus['addmenus']) {
                foreach ($menus['addmenus'] as $key => $imenu) {
                    Db::table('frame_admin_menu')
                        ->insertGetId($imenu);
                }
            }

            //编辑
            if (isset($menus['editmenus']) && $menus['editmenus']) {

                foreach ($menus['editmenus'] as $key => $imenu) {
                    Db::table('frame_admin_menu')
                        ->where('ident', '=', $imenu['ident'])
                        ->update($imenu);
                }
            }

            Db::commit();
        } catch (Throwable $ex) {
            Db::rollBack();
            throw $ex;
        }
    }


    private function installUcMenu(array $menus)
    {
        Db::beginTransaction();
        try {

            //删除 
            if (isset($menus['delmenus']) && $menus['delmenus']) {
                foreach ($menus['delete'] as $key => $imenu) {
                    Db::table('frame_ucenter_menu')
                        ->where('ident', '=', $imenu['ident'])
                        ->delete();
                }
            }


            //新增
            if (isset($menus['addmenus']) && $menus['addmenus']) {
                foreach ($menus['addmenus'] as $key => $imenu) {
                    Db::table('frame_ucenter_menu')
                        ->insertGetId($imenu);
                }
            }

            //编辑
            if (isset($menus['editmenus']) && $menus['editmenus']) {

                foreach ($menus['editmenus'] as $key => $imenu) {
                    Db::table('frame_ucenter_menu')
                        ->where('ident', '=', $imenu['ident'])
                        ->update($imenu);
                }
            }

            Db::commit();
        } catch (Throwable $ex) {
            Db::rollBack();
            throw $ex;
        }
    }


    /**
     * 安装事件动作
     *
     * @param string $pluginname
     * @return array
     */
    private function installHook(array $intsall_eventaction)
    {
        Db::beginTransaction();
        try {

            //删除 
            if (isset($intsall_eventaction['delete']) && $intsall_eventaction['delete']) {
                foreach ($intsall_eventaction['delete'] as $key => $event) {
                    Db::table('frame_hook')
                        ->where('ident', '=', $event['ident'])
                        ->delete();
                }
            }

            //编辑 
            if (isset($intsall_eventaction['edit']) && $intsall_eventaction['edit']) {
                foreach ($intsall_eventaction['edit'] as $key => $event) {
                    $event['param'] = isset($event['param']) && $event['param'] ?
                        json_encode($event['param'], JSON_UNESCAPED_UNICODE) :
                        json_encode([], JSON_UNESCAPED_UNICODE);

                    Db::table('frame_hook')
                        ->where('ident', '=', $event['ident'])
                        ->update($event);
                }
            }

            //添加 
            if (isset($intsall_eventaction['add']) && $intsall_eventaction['add']) {
                foreach ($intsall_eventaction['add'] as $key => $event) {
                    $event['param'] = isset($event['param']) && $event['param'] ?
                        json_encode($event['param'], JSON_UNESCAPED_UNICODE) :
                        json_encode([], JSON_UNESCAPED_UNICODE);
                    Db::table('frame_hook')
                        ->where('ident', '=', $event['ident'])
                        ->insert($event);
                }
            }


            Db::commit();
        } catch (Exception $ex) {
            Db::rollBack();
            throw $ex;
        }
    }






    /**
     * 获取安装的描述文件
     *
     * @param string $pluginident 插件英文名
     * @return array
     */
    private function getDescribe(string $pluginident): array
    {
        $describe = include base_path() . '/plugin/' . $pluginident . '/describe.php';
        $logo     = base_path() . '/plugin/' . $pluginident . '/logo.png';
        if (file_exists($logo)) {
            $imageEncoder = Base64ImageEncoder::fromFileName(
                $logo,
                ['jpeg', 'png', 'gif']
            );
            $describe['logo'] =  $imageEncoder->getDataUri();
        } else {
            $describe['logo'] = '';
        }

        $describe['ident'] = $pluginident;
        return $describe;
    }



    /**
     * 释放压缩包
     *
     * @param string $fileinfo
     * @param string $pluginident
     * @return string
     */
    private function extractZip(array $fileinfo, string $pluginident, string $type = 'install'): string
    {
        $zipFile = new ZipFile();
        $zipFile->openFile(base_path() . $fileinfo['path']);

        $filelists = $zipFile->getListFiles();


        if ($type == 'install' && !in_array('source/plugin/' . $pluginident . '/describe.php', $filelists)) {
            throw new Exception('组件不正确，缺少配置文件');
        }

        $pluginpath = base_path() . 'plugin/' . $pluginident;
        if ($type == 'install') {
            if (file_exists($pluginpath)) {
                throw new Exception('已经安装过过或存在相同的应用');
            }
        }

        $extractpath = runtime_path() . '/storage/' . ($type == 'install' ? 'install' : 'upgrade') . '/plugin/' . $pluginident . '/' . $fileinfo['newfilename'];
        if (!file_exists($extractpath)) {
            mkdir($extractpath, 0777, true);
        }

        $zipFile->extractTo($extractpath);

        return $extractpath;
    }

    /**
     * 清理安装升级包
     *
     * @param string $extractpath
     * @param string $zipfile
     * @return void
     */
    private function cleanInstallUpgradeFiles(string $extractpath, string $zipfile)
    {
        try {
            if (file_exists($extractpath)) {
                FileHelper::delDir($extractpath);
            }
            if (file_exists($zipfile)) {
                FileHelper::delFile($zipfile);
            }
        } catch (Exception $ex) {
        }
    }


    /**
     * 检查是否有版本更新
     *
     * @param integer|array $plugin_or_id
     * @return boolean
     */
    public function checkUpgrade(int|array $plugin_or_id): bool
    {
        $plugin = $plugin_or_id;
        if (is_int($plugin_or_id)) {
            $plugin = $this->find($plugin_or_id);
        }
        $upgrade = $plugin['upgrade'];

        if ($upgrade == '') {
            return false;
        }

        $libInstallerGeneral = new LibInstallerGeneral();
        $nextVersion = $libInstallerGeneral->getNextUpgradeVersion($upgrade, $plugin['version']);

        if ($nextVersion) {
            Db::table('frame_plugin')
                ->where('id', '=', $plugin['id'])
                ->update([
                    'nextversion' => $nextVersion['nextVersion']
                ]);
            return true;
        }
        return false;
    }

    /**
     * 批量检查是否有版本更新
     *
     * @return void
     */
    public function checkAllUpgrade()
    {
        $plugins = $this->select();
        foreach ($plugins as $key => $plugin) {
            $this->checkUpgrade($plugin);
        }
    }


    /**
     * 分页查询
     *
     * @param string $name
     * @param string $ident
     * @param integer $pagesize
     * @return LengthAwarePaginator
     */
    public function paginator(
        string $title = '',
        int $pagesize = 20
    ): LengthAwarePaginator {
        return  Db::table('frame_plugin')
            ->when($title != '', function ($query) use ($title) {
                return $query->where('title', 'like', '%' . $title . '%');
            })
            ->orderBy('id', 'desc')
            ->paginate($pagesize);
    }
}
